Apple, the Apple logo, and Macintosh are registered trademarks of Apple Computer, Inc.
Mac and OpenDoc are trademarks of Apple Computer, Inc.
ODNameResolver::IsODToken and compare procs
When your compare proc is called, the two input parameters may be either ODDescs that describe data, or ODOSLTokens created by one of your object accessors. To distinguish them, use the call IsODToken on the input parameters.
Part IDs
The storage subsystem has support for unique IDs for persistent objects. The Semantic Events subsystem can make use of these for finding embedded parts. A “part” ID is really a frame ID for any part that has display frames. However, if a part has no display frames, that part may be referred to by its own persistent ID.
The “Default” Semantic Interface
Default event handling
The default semantic interface contained in the Semantic Events subsystem attempts to handle events and other callback procs for shells or parts which no semantic interface, or which have returned an error from their semantic interface while trying to handle an event. However, it only attempts to handle certain events and only under strict conditions.
The default semantic interface will return info about the standard properties that every part supports. These properties are enumerated in ODRegtry.idl and the ‘aete’ resource in the OpenDoc Shell shared library.
The only event handled is the GetData (Class: kCoreEventClass; ID: kAEGetData) event. And it’s only handled for the standard part properties (see above).
Three accessors are supported:
•Container: null; Object: cPart—this accessor will return a standard embedded frame token. It handles the key forms formName, formUniqueID and formPosition. If formName or formUniqueID are specified, the entire containment hierarchy is searched starting from the embedded frames of the container. The one exception occurs at the level of the root part. If the default accessors are asked to resolve an object specifier whose first chunk describes a part with formName or formUniqueID, the root part will be searched as well as the an embedded frames in the root part.
•Container: null; Object: cProperty—this accessor will return a reference to a particular part property if the property is one that OpenDoc knows about (see above).
•Container: cPart; Object: typeWildCard—this accessor will return a swap token so that context switching to the embedded part may occur.
These accessors will only attempt to return a token when they understand the contents of the container token. This happens when the container token is either a token that another default accessor created (there is a private internal flag in the token for this), or is a NULL token or a standard embedded frame token.
Intelligent adjustment of the NULL context.
The default semantic interface will attempt to do some intelligent adjustment of the top level object specifier resolution context (the NULL context). When the resolution of the direct parameter of the object specifier is attempted, the documemt shell is first to have a crack at resolving the first chunk of the object specifier. If it fails, we assume that the root part is appropriate place to begin the resolution.
For scripting purposes, the root part is semantically equivalent to the document, and in most cases the application shell as well. However, there are some objects that only the document shell can handle, such as windows.
So, for example, if the first chunk of the object specifier describes the first window of the application, then it makes sense for the document shell to act as the NULL context and return a reference to that window. However, if the first chunk describes the first word, this makes no sense in the context of the document shell and so it will be assumed that the user meant the first word of the document (root part). The root part will then be asked to return a reference to the first word.
(Please note that in release 1.0, there is no support for the window object in the document shell.)
Part to part communication (Addressing Parts)
There ar e a number of ways to send Semantic Events from one part to another. Each method is appropriate for its own set of cirucumstances.
Using ODMessageInterface::CreatePartObjSpec
This method will create an object specifier appropriate for using as the direct parameter of a Semantic Event that is to be targeted to the part in question. Note that the object specifier created is a special object specifier that the Semantic Events subsystem looks for explicitly when dispatching and which causes a fast dispatch directly to the part without any further resolution (i.e., ODNameResolver::Resolve is never called.) This object specifier should never be inspected or used for recording purposes. You should not try to use the object specifier as a piece of another larger object specifier.
Using EmbeddedFrameSpec
This method can be used to create an inspectable object specifier that is also appropriate for using as the direct parameter of a Semantic Event. It can be used for recording as well. The part is responsible for implementing this method. The destination frame’s container’s method should be called. This method should be implemented to return an object specifier that best describes (preferably in a unique way) the embedded frame parameter. This might be accomplished by creating an object specifier that describes the frame’s unique ID. Another way to implement this might be to call the container’s EmbeddedFrameSpec, passing in this part’s frame as a parameter, and then tack on the original container part’s description of the embedded frame to the object specifier. This will will create a hierarchical description of the frame to which to send the part.
If the container doesn’t implement this call, or if the event is to be sent to a part with no display frames, the part wishing to send the event should make its own object specifier using the unique ID of the destination frame or part.
Describing contents or properties of parts
The two methods above describe creating an object specifier that describes a part or frame. If you need to describe a property or content object of the part, you should first use the method above described under “EmbeddedFrameSpec” to get a reference to the part or frame and then add on the piece of the object specifier that refers to the property or content of the part. You will most likely have to do this for recording purposes.
ODSemanticInterface and related utility classes
In order to enable scripting in your part, you must instantiate a subclass of the abstract base class ODSemanticInterface. You can create your own subclass or use a “pre-brewed” one. If you create your own subclass, you must take care to make the name of the class unique. If two part editors attempt to instantiate two different SOM classes that have the same name, bad things will happen.
OpenDoc provides a pre-brewed subclass called ODCPlusSemanticInterface with its unsupported utilities. This class uses the services of two C++ classes, SIHelperAbs and SIHelper. If y ou use ODCPlusSemanticInterface, you must also take care to make copies of the IDL and source files and rename the class for SOM. When using this suite of utility classes, you will use the API of SIHelper to install and remove callback functions. The methods of this class attempt to mimic the API of the Apple event Manager and the OSL. Note that these classes are provided as unsupported utilities. They are not necessary for supporting scripting in your part. The depend on C++ and cannot be used from C. There are certainly other ways that this code could have been implemented. Some of the other utility classes may be useful for implementing dispatching should you choose to create our own ODSemanticInterface subclass.
One suggestion for creating a unique class name for your subclass is to use the unique four character code assigned to you by DTS.
If you are only handling a few events and/or a few Apple event objects, it is probably easiest to create your own subclass of ODSemanticInterface. To handle an event, you would put your event handling code in your override to the CallEventHandler method. ODSemanticInterface provides no dispatching services of its own. Therefore, the CallEventHandler method will get called for all events that your part handles. In order to dispatch to your code that handles a particular type of event, you will probably have some code that looks like this:
Other methods of dispatching are possible of course. A simple if…else clause will probably suffice if you only handle a few events. A similiar dispatching scheme is needed in your subclass of the CallObjectAccessor method.
Standard token for embedded part
If you are going to return a token that represents another part, you should return the “standard part token”. The format of this token is as follows. The descriptorType should be set to kODStandardPartTokenType ('part'). The first four bytes of the dataHandle should be set to the ODFrame* for the part (if one exists), and the second four bytes should contain an ODPart*. When OpenDoc needs to know if a token refers to a part, it will examine the descriptorType first, and, if it doesn’t find a descriptorType of kODStandardPartTokenType , will attempt to coerce the token to a standard part token. If you furnish the appropriate coercion handler, you can represent a part in any way you desire.
ODPart* parameter in ODSemanticInterface calls
Most routines in ODSemanticInterface have an ODPart* as their first parameter. This ODPart* is actually an ODPartWrapper*. However, the part’s real ODPart* may be found in the fBase field of the ODSemanticInterface object and can be obtained by called ODSemanticInterface::GetBase (inherited from ODExtension).
Swapping in count procs
Sometimes it happens that your part’s count proc is called to count items with a container that represents an embedded part. You will probably be unable to count the objects requested because you have no knowledge of the internal content of your embedded parts. The embedded part should be asked to do the counting instead. If you detect this case, you can return -1 for the result of count proc. OpenDoc will then attempt to ask the embedded part to do the counting.
Handling events for embedded parts
Sometimes you will receive an event that should be handled by one of your embedded parts. For example, say you had a document that had one embedded part in the root part, and that part was a movie part. The script
tell application "My OpenDoc Document"
play part 1 -- where part 1 is the movie part
end tell
will send the play event to the root part. However, only the embedded movie part knows how to play itself. If you return errAEEventNotHandled to OpenDoc, OpenDoc will retry sending the event to the embedded part.
Why doesn’t OpenDoc always send the event to the correct part? Because the semantics of each event is different. For example, consider this scriptlet:
tell application "My OpenDoc Document"
delete part 1
end tell
In this case the root part, which is the containing part, should definitely handle the delete of its embedded part.